package com.owc.filter;

import com.owc.common.context.JwtTokenHelper;
import com.owc.common.context.SessionHelper;
import com.owc.common.enums.ExceptionEnum;
import com.owc.common.exception.HeaderException;
import com.owc.common.exception.LoginException;
import com.owc.common.exception.NoPermissionException;
import com.owc.common.exception.OWCException;
import com.owc.common.redis.RedisUtil;
import com.owc.common.tool.ApiRequestSupport;
import com.owc.common.tool.DateUtils;
import com.owc.common.Const.HeaderConst;
import com.owc.filter.authentication.AuthenticationImpl;
import com.owc.filter.content.SecurityContextHolder;
import com.owc.filter.content.SecurityContextImpl;
import com.owc.filter.content.UserDetail;
import com.owc.service.app.PermissionService;
import com.owc.service.app.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Optional;

/**
 * Request permission valid.
 */
@Configuration
@Slf4j
public class RequestValidInterceptor extends HandlerInterceptorAdapter {

    private final UserService userService;
    private final PermissionService permissionService;

    public RequestValidInterceptor(UserService userService,PermissionService permissionService) {
        this.userService = userService;
        this.permissionService = permissionService;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.debug("RequestValidInterceptor  Content-type:[{}] ......" ,request.getContentType());
        if(Boolean.parseBoolean(SessionHelper.getRequestHeader(HeaderConst.dev)
                .filter(s -> !s.isEmpty())
                .orElse("false"))){
            loadDefaultUser();
            return true;
        }
        try {

            String token = SessionHelper.getRequestToken();
            if(StringUtils.isBlank(token)){
                throw new HeaderException("Please login again.");
            }

            String username = JwtTokenHelper.getUsernameFromToken(token);
            if(StringUtils.isBlank(username)){
                throw new NoPermissionException("Authentication failed");
            }

            if(JwtTokenHelper.isTokenExpired(token)){
                throw new NoPermissionException("Token has expired.");
            }

            Optional<UserDetail> optUser = permissionService.getUserDetailByClient(username,SessionHelper.getRequestClient());
            if(!optUser.isPresent()){
                throw new NoPermissionException("Account does not exist or has been disabled");
            }

            if(!JwtTokenHelper.validateToken(token, optUser.get())){
                throw new NoPermissionException("Token expired or has expired");
            }

            //Check if you are logged in on another client
            if(checkIsOtherLogin(token,optUser.get().getUser().getId())){
                throw new LoginException(ExceptionEnum.OTHER_LOGIN_ERR);
            }

            log.info("Request valid passed   <<<<<<<<<<>>>>>>>>>>>>>>   username:[{}] expireDate:[{}]",username, DateUtils.formatYMDHMS(JwtTokenHelper.getExpirationDateFromToken(token)));

            SecurityContextImpl context = optUser
                    .map(userDetail -> new SecurityContextImpl(new AuthenticationImpl(userDetail)))
                    .orElseThrow(() -> new NoPermissionException("Token parser failed. Please login again."));

            // Set security
            SecurityContextHolder.setContext(context);
        }  catch (OWCException e) {
            log.error("Interceptor throw exception : " + "\t" + e.getMessage());
            ApiRequestSupport.invokeExceptionWrapper(response, e.getCode(),e.getMessage());
            return false;
        }
        return true;
    }

    private boolean checkIsOtherLogin(String token, Long userId) {
        String cacheToken = userService.getUserJwtToken(userId);
        return cacheToken != null && !cacheToken.equals(token);
    }

    private void loadDefaultUser() {
        SecurityContextHolder.createTestUser();
    }
}
